//#include "opengl.h"
#include "emulador.h"
#include <mpi.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

MPI_Status status;
double distanciaPontos, distanciaBolas;
int rank, ncpus, loop = 0, i, k, l;
TBola *bolas;
MPI_Datatype MPI_Ball;
MPI_Datatype type[2] = { MPI_DOUBLE, MPI_INT };
MPI_Aint disp[2];
int blocklen[2] = { 14, 1 };
int base;

void sincronizar();
void verificarColisoes(TBola *bolas, int numBolas, int rank);

int main(int argc, char** argv) {

	MPI_Comm p_quadrante;
	MPI_Comm s_quadrante;
	MPI_Comm t_quadrante;
	MPI_Comm q_quadrante;

	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	MPI_Comm_size(MPI_COMM_WORLD, &ncpus);

	bolas = (TBola*) malloc(ncpus * sizeof(TBola));

	MPI_Get_address(bolas, disp);
	MPI_Get_address(&bolas[0].reserved, disp + 1);
	base = disp[0];
	for (i = 0; i < 2; i++)
		disp[i] -= base;

	MPI_Type_struct(2, blocklen, disp, type, &MPI_Ball);

	MPI_Type_commit(&MPI_Ball);

	//verificarColisoes(0, 40, 0, 90, bolas, rank, 5.0f, 5.0f);

	emulador_criarBola(20 + 10 * rank, 40, 50, 100, bolas, rank, 5.0f, 5.0f);
	//esperando cada processo criar a sua bola
	MPI_Barrier(MPI_COMM_WORLD);
	while (loop < 1000) {
		play(&bolas[rank]);
		//if (rank%2 == 1) {
		if (rank == 0) {
			/*MPI_Send(bolas, 1, MPI_Ball, (rank+1)%ncpus, 1, MPI_COMM_WORLD);
			 MPI_Recv(bolas, 1, MPI_Ball, (rank-1+ncpus)%ncpus, 1, MPI_COMM_WORLD, &status);*/
			/*for (i = 0; i < ncpus; i++) {
			 printf("%4.2f %4.2f ", bolas[i].sx, bolas[i].sy);
			 }*/

			printf("\n");
		} else {
			/*MPI_Send(bolas, 1, MPI_Ball, (rank-1+ncpus)%ncpus, 1, MPI_COMM_WORLD);
			 MPI_Recv(bolas, 1, MPI_Ball, (rank+1)%ncpus, 1, MPI_COMM_WORLD, &status);*/
		}
		// sincronize aqui!!
		/*if (MPI_Allgather(MPI_IN_PLACE, 1, MPI_Ball, bolas, 1, MPI_Ball,
		 MPI_COMM_WORLD) != MPI_SUCCESS) {
		 printf("RANK %i: error gathering 1\n", rank);
		 MPI_Finalize();
		 exit(0);
		 }*/
		if (MPI_Gather(bolas + rank, 1, MPI_Ball, bolas, 1, MPI_Ball, 0,
				MPI_COMM_WORLD) != MPI_SUCCESS) {
			printf("RANK %i: error gathering 1\n", rank);
			MPI_Finalize();
			exit(0);
		}
		/*for (l = 1; l < ncpus; ++l) {

		 }*/
		if (rank == 0) {
			/*MPI_Send(bolas, 1, MPI_Ball, (rank+1)%ncpus, 1, MPI_COMM_WORLD);
			 MPI_Recv(bolas, 1, MPI_Ball, (rank-1+ncpus)%ncpus, 1, MPI_COMM_WORLD, &status);*/
			for (i = 0; i < sizeof(bolas); i++) {
				printf("%4.2f %4.2f ", bolas[i].sx, bolas[i].sy);
			}

			printf("\n");
		}

		verificarColisoes(bolas, ncpus - 1, rank);
		loop++;
	}
	MPI_Type_free(&MPI_Ball);
	MPI_Finalize();

	//posInicialX,posInicialY, velocidadeImpulso, angulo, TBola,   indexBola, double massa, double raio) {
	//emulador_criarBola(0,0,80,30,&bolas,0,5,5);

	//Codigo rodando serial
	/*TBola bolas[10];
	 emulador_criarBola(10, 40, 50, 90, &bolas, 0, 5.0f, 5.0f);
	 debug(&bolas[0]);*/

	//emulador_criarBola(0, 90, 0, 90, &bolas, 1, 5.0f, 5.0f);
	//emulador_criarBola(0,0,80,30,&bolas,2,0.20,6);
	//emulador_criarBola(50,90,0,90,&bolas,4,0.20,5);

	//execOpenGL(argc, argv, &bolas,2);
}
/*void verificarColisoes(TBola *bolas, int numBolas, int rank) {
 int i, j;
 double distanciaPontos, distanciaRaio;
 //outra forma de calcular é pela equação reduzida da circunferência será determinada por r^=(bsx-asx)^2 + (bsy-asy)^2
 //if ( (pow (bolas[j].sx - bolas[i].sx,2) + pow(bolas[j].sy - bolas[i].sy, 2)) <= pow(bolas[i].raio + bolas[j].raio, 2 ) )
 for (i = 1; i < rank; i++)
 for (j = i + 1; j < numBolas; j++)
 if (bolas[i].raio > 0 && bolas[j].raio > 0) {
 distanciaPontos = sqrt(pow(bolas[j].sx - bolas[i].sx, 2) + pow(
 bolas[j].sy - bolas[i].sy, 2)); //dúvida no cálculo
 distanciaRaio = bolas[i].raio + bolas[j].raio;
 if (distanciaPontos <= distanciaRaio) {
 #ifdef DEBUG
 printf("\nDistanciaPontos:%4.2f, DistanciaRaio:%4.2f\n",distanciaPontos,distanciaRaio);

 #endif
 //comunicacao entre as bolas que irao colidir
 MPI_Send(bolas + i, 1, MPI_Ball, i, 1, MPI_COMM_WORLD);
 MPI_Recv(bolas + i, 1, MPI_Ball, j, 1, MPI_COMM_WORLD, &status);

 MPI_Send(bolas + j, 1, MPI_Ball, j, 1, MPI_COMM_WORLD);
 MPI_Recv(bolas + j, 1, MPI_Ball, i, 1, MPI_COMM_WORLD, &status);


 colidirBolas(&bolas[i], &bolas[j]);
 }

 #ifdef DEBUG
 printf("check B1: ");
 printbola(&bolas[i]);
 printf("check B2: ");
 printbola(&bolas[j]);
 #endif

 }
 }*/
void verificarColisoes(TBola bolas[], int numBolas, int rank) {
	int i, j;
	double distanciaPontos, distanciaRaio;
	//outra forma de calcular é pela equação reduzida da circunferência será determinada por r^=(bsx-asx)^2 + (bsy-asy)^2
	//if ( (pow (bolas[j].sx - bolas[i].sx,2) + pow(bolas[j].sy - bolas[i].sy, 2)) <= pow(bolas[i].raio + bolas[j].raio, 2 ) )
	for (i = 1; i < rank; i++)
		for (j = i + 1; j < numBolas; j++)
			if (bolas[i].raio > 0 && bolas[j].raio > 0) {
				distanciaPontos = sqrt(pow(bolas[j].sx - bolas[i].sx, 2) + pow(
						bolas[j].sy - bolas[i].sy, 2));
				distanciaRaio = bolas[i].raio + bolas[j].raio;
				if (distanciaPontos <= distanciaRaio) {
					/*#ifdef DEBUG
					 printf("\nDistanciaPontos:%4.2f, DistanciaRaio:%4.2f\n",distanciaPontos,distanciaRaio);
					 #endif*/
					//comunicacao entre as bolas que irao colidir
					MPI_Send(bolas + i, 1, MPI_Ball, i, 1, MPI_COMM_WORLD);
					MPI_Recv(bolas + i, 1, MPI_Ball, j, 1, MPI_COMM_WORLD,
							&status);

					MPI_Send(bolas + j, 1, MPI_Ball, j, 1, MPI_COMM_WORLD);
					MPI_Recv(bolas + j, 1, MPI_Ball, i, 1, MPI_COMM_WORLD,
							&status);

					colidirBolas(&bolas[i], &bolas[j]);
				}
			}
}

